package base.jdk8_demo.lambda.demo07;

import base.jdk8_demo.lambda.demo02.Emp;
import base.jdk8_demo.lambda.demo02.Status;
import org.junit.Test;

import java.nio.file.Files;
import java.sql.SQLOutput;
import java.time.Duration;
import java.time.Instant;
import java.time.LocalDateTime;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Pattern;
import java.util.stream.IntStream;
import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * Stream 是jdk8 中处理集合的抽象概念，它可以指定你希望对集合进行的操作，可以执行非常复杂的查找、过滤、
 * 和映射数据操作。
 *
 * 1、Stream的三个操作步骤
 *  1.1 创建 Stream
 *  1.2 中间操作
 *  1.3 终止操作
 */
public class TestStream {
    List<Emp> emps = Arrays.asList(
            new Emp("ww", 20, 2222.0),
            new Emp("ls", 30, 3333.0),
            new Emp("zs", 35, 5555.0),
            new Emp("zl", 40, 7777.0),
            new Emp("tq", 56, 6666.0),
            new Emp("tq", 56, 6666.0),
            new Emp("tq", 56, 6666.0),
            new Emp("tom", 25, 8888.0)
    );


    /**
     * 1.1 Stream 的创建几种方式
     */
    @Test
    public void createStream() {
        //1.可以通过 java.util.Collection 系列集合提供的 stream() 或 parallelStream()
        // stream 代表串行流，parallelStream 代表并行流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();
        System.out.println(stream1);

        System.out.println("---------------------------2---------------------------");
        //2.通过 java.util.Arrays 中的静态方法 stream() 获取数组流
        Emp[] emps = new Emp[10];
        Stream<Emp> stream2 = Arrays.stream(emps);
        System.out.println(stream2);

        System.out.println("---------------------------3---------------------------");
        //3.通过 Stream 类中的静态方法 of()、iterate() 获取 或者从上流类静态工厂方法获取
        Stream<String> stream3 = Stream.of("zhou","jia","wen","aa", "bb", "cc");
        System.out.println(stream3);
        stream3.forEach(System.out::println);

        System.out.println("---------------------------4---------------------------");
        //4.创建无限流
        //迭代
//        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2).parallel();
        Stream<Integer> stream4 = Stream.iterate(0, (x) -> x + 2);
        System.out.println(stream4);

        System.out.println("---------------------------5---------------------------");
        // 5个数字，从0开始，每位递增2.这里不用limit限定，会无限制的递增下去
        stream4.limit(5)
                .forEach(System.out::println);

        //生成
        Stream<Double> stream5 = Stream.generate(() -> Math.random());
        System.out.println(stream5);
        // 随机5个double数字
        stream5.limit(5)
                .forEach(System.out::println);

        //
        IntStream stream6 = IntStream.range(0, 10);
//        stream6.forEach((x)-> System.out.println(x));
        stream6.forEach(System.out::println);

        //5、文件的行可以从BufferedReader.lines()获取
        //6、文件路径的流 Files.lines(Path)
        System.out.println("---------------------------------7----------------------------");
        //7、随机数流 java.util.Random.ints()
        new Random().ints(5).forEach(System.out::println);
        //8、许多其它的数据流包括 BitSet.stream() ,Pattern.splitAsStream(java.lang.CharSequence)和JarFile.stream()
        final Stream<String> ss = Pattern.compile(" ").splitAsStream("zhou jia wen");
        ss.forEach(System.out::println);
    }



    /**
     * 1.2 Stream的中间操作
     */
    @Test
    public void streamOperation(){
        /**
         * filter 筛选，接收 lambda，从流中排除某些元素
         */
        Stream<Emp> stream1 = emps.stream()
                .filter((e) -> {
                    System.out.println("Stream 中间操作过滤");
                    return e.getAge() >= 35;
                });

        // Stream 终止操作
//        stream1.forEach((x)-> System.out.println(x));
        //已实现的方法(println())和函数式接口中方法入参及返回值保持一致，可以使用方法引用
        stream1.forEach(System.out::println);

        System.out.println("-------------------limit-------------------");
        /**
         * limit 截断流，使其元素不超过给定数量
         */
        emps.stream()
                .filter((emp)-> {
                	System.out.println("---------filter------------");
                	return emp.getSalary()>5000;
                })
                .limit(2)
                .forEach(System.out::println);

        System.out.println("-------------------skip-------------------");
        /**
         * skip(n) 跳过元素，返回一个扔掉前n个元素的流，若流中元素不足n个，则返回一个空流，与 limit(n) 互补
         */
        emps.stream()
                .filter(e -> {
                	System.out.println("---------filter------------");
                	return e.getSalary()>5000;
                })
                .skip(2)
                .forEach(System.out::println);

        System.out.println("---------------------distinct---------------------");
        /**
         * distinct 筛选去重，通过流所生成元素的hashCode() 和 equals() 去除重复元素
         */
        emps.stream()
                .filter(e -> {
                	System.out.println("---------filter------------");
                	return e.getSalary()>5000;
                })
                .distinct()
                .forEach(System.out::println);

        emps.stream()
                .filter(e -> e.getSalary() >=5000)//此步过滤掉了工资低于5000的
                .filter(e -> e.getAge() >=35)//此步在上一步基础上过滤,过滤掉的数据不会再被过滤
                .distinct()
                .forEach(System.out::println);

        emps.stream()
                .filter(e -> {
                    return e.getSalary()>=5000 && e.getAge()>=35;
                })
                .distinct()
                .forEach(System.out::println);
    }

    /**
     * 映射
     * map -接收 lambda， 将元素转换成其他形式或提取信息。接收一个函数作为参数，该函数会被应用到每个元素上，并将其
     * 映射成一个新的元素。
     *
     * flatMap -接收一个函数作为参数，将流中的每个值都换成另一个流，然后把所有流连接成一个流。
     *
     */
    @Test
    public void mapTest(){
        List<String> list = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        list.stream()
                .map(x -> x.toUpperCase())
                .forEach(System.out::println);

        System.out.println("------------------111--------------------------");
        emps.stream()
//                .map(emp -> emp.getName())
                .map(Emp::getName)
                .forEach(System.out::println);

        System.out.println("-------------------222-------------------------");
        // reflect: 由.map()方法可知，Function.apply(string){}实现是通过TestStream::filterCharacter来实现的，
        Stream<Stream<Character>> ssm = list.stream()
//                .map((str) -> TestStream.filterCharacter(str));
                .map(TestStream::filterCharacter);//{ {a,a,a}, {b,b,b},...}
        ssm.forEach((sm) -> {
            sm.forEach(System.out::println);
        });
        System.out.println("--------------------通过flatMap改造------------------------");
        Stream<Character> cs = list.stream()
//        		.flatMap(x -> TestStream.filterCharacter(x));// {a,a,a,b,b,b,.....}
                .flatMap(TestStream::filterCharacter);// {a,a,a,b,b,b,.....}
        cs.forEach(System.out::println);
    }

    public static Stream<Character> filterCharacter(String str){
        List<Character> list = new ArrayList<>();

        for (Character c : str.toCharArray()) {
            list.add(c);
        }
        return list.stream();
    }


    /**
     * 排序
     * sorted() -自然排序（Comparable），Comparable里面的compareTo()方法进行比较
     * sorted(Comparator) -定制排序
     */
    @Test
    public void sortTest(){
        List<String> list = Arrays.asList("cc","aa","bb","gg","ff");
        list.stream()
                .sorted()
                .forEach(System.out::println);
        System.out.println("-----------------------------------------------");
        emps = Arrays.asList(
                new Emp("ww", 20, 2222.0),
                new Emp("ls", 30, 3333.0),
                new Emp("zs", 35, 5555.0),
                new Emp("zl", 40, 7777.0),
                new Emp("tq", 56, 6666.0),
                new Emp("tq", 56, 8888.0),
                new Emp("tq", 56, 9999.0)
        );
        emps.stream()
                .sorted((e1,e2)->{
                    if(e1.getAge().equals(e2.getAge())){
                        return e1.getSalary().compareTo(e2.getSalary());
                    }else{
                        return e1.getAge().compareTo(e2.getAge());
                    }
                }).forEach(System.out::println);
    }




    @Test
    public void testNormal(){
        List<Emp> emps = Arrays.asList(
                new Emp("zs", 35, 5555.0, Status.FREE),
                new Emp("ls", 30, 3333.0, Status.FREE),
                new Emp("ww", 20, 6666.0, Status.FREE),
                new Emp("zl", 40, 4444.0, Status.FREE),
                new Emp("tb", 36, 6666.0, Status.FREE),
                new Emp("tq", 56, 6666.0, Status.FREE),
                new Emp("tj", 46, 6666.0, Status.FREE)
        );

        //并行流，采用多线程处理，因此输出结果不确定
        Optional<Emp> any = emps.parallelStream()
                .filter(e -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(any.get());

        System.out.println("----------------111-----------------");
        // 返回此元素流的迭代器。
        Iterator<Emp> iterator = emps.parallelStream().iterator();
        while (iterator.hasNext()) {
            System.out.println(iterator.next());
        }

        System.out.println("----------------222-----------------");
        // Stream可变构建器。 这允许创建的Stream通过单独发电元件，并将它们添加到Builder （而没有来自使用复制的开销ArrayList作为临时缓冲器）。
        Stream.Builder<String> builder = Stream.builder();
        builder = builder.add("aa")
                 .add("bb")
                 .add("cc");
        Stream<String> stream = builder.build();

        System.out.println("----------------333-----------------");
        //map...reduce 大数据并行处理，替换jdk1.7的fork...join(难写)
        LocalDateTime start = LocalDateTime.now();
        long sum = LongStream.range(0, 100000000L)
                .parallel()
                .reduce(0, (x, y) -> x + y);
        System.out.println(sum);
        LocalDateTime end = LocalDateTime.now();
        System.out.println("耗时："+ Duration.between(start, end).toMillis());
        System.out.println(LocalDateTime.now());

    }





}
